package it.koutifaris.travianmap.gui;

import android.util.Log;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
import android.view.ScaleGestureDetector;
import android.view.ScaleGestureDetector.OnScaleGestureListener;

public class MapViewGestureListener extends SimpleOnGestureListener implements OnScaleGestureListener {
	private MapView mv;
	private float touchX;
	private float touchY;
	private int startdX;
	private int startdY;
	private int startSqDim;
	private Fling fling;
	private boolean scaling = false;

	public MapViewGestureListener(MapView mv) {
		this.mv = mv;
	}

	@Override
	public boolean onDown(MotionEvent e) {
		if (fling != null)
			fling.stop();
		if (scaling)
			return true;

		Log.i(this.getClass().getName(), "onDown");
		touchX = e.getX();
		touchY = e.getY();
		startdX = mv.getdX();
		startdY = mv.getdY();

		return true;
	}

	@Override
	public boolean onSingleTapConfirmed(MotionEvent e) {
		if (scaling)
			return true;

		Log.i(this.getClass().getName(), "onSingleTapConfirmed");

		// mv.

		return true;
	}

	@Override
	public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
		if (scaling)
			return true;

		Log.i(this.getClass().getName(), "onScroll");
		mv.setdX((int) (e2.getX() - touchX) + startdX);
		mv.setdY((int) (e2.getY() - touchY) + startdY);
		mv.invalidate();

		return true;
	}

	@Override
	public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
		if (scaling)
			return true;

		Log.i(this.getClass().getName(), "onFling");
		fling = new Fling(velocityX, velocityY);
		fling.start();

		return true;
	}

	@Override
	public boolean onScale(ScaleGestureDetector detector) {
		scaling = true;

		float mScaleFactor = mv.getZoom() * detector.getScaleFactor();
		mScaleFactor = Math.max(0.5f, Math.min(mScaleFactor, 2.0f));
		Log.i(this.getClass().getName(), "onScale" + mScaleFactor);
		int sqx = startdX / startSqDim;
		int sqy = startdY / startSqDim;
		int dx = startdX % startSqDim;
		int dy = startdY % startSqDim;
		mv.setZoom(mScaleFactor);
		mv.setdX(sqx * mv.getSqDim() + dx);
		mv.setdY(sqy * mv.getSqDim() + dy);
		mv.invalidate();

		return true;
	}

	@Override
	public boolean onScaleBegin(ScaleGestureDetector detector) {
		scaling = true;

		Log.i(this.getClass().getName(), "onScaleBegin");
		startdX = mv.getdX();
		startdY = mv.getdY();
		startSqDim = mv.getSqDim();

		return true;
	}

	@Override
	public void onScaleEnd(ScaleGestureDetector detector) {
		scaling = false;
		Log.i(this.getClass().getName(), "onScaleEnd");
	}

	private class Fling {
		private static final float SCALE_V = 1f;
		private static final float A = 800f;
		private float vx0, vy0, x0, y0;
		private float ay, ax;
		private boolean stop = false;
		private float t, vy, vx, ySign, xSign;
		private long start;

		public Fling(float vx0, float vy0) {
			vy0 = vy0 * SCALE_V;
			vx0 = vx0 * SCALE_V;
			this.vx0 = vx0;
			this.vy0 = vy0;
			this.x0 = mv.getdX();
			this.y0 = mv.getdY();
			double arc = Math.atan2(vy0, vx0);
			this.ay = (float) (-A * Math.sin(arc));
			this.ax = (float) (-A * Math.cos(arc));
		}

		public void stop() {
			stop = true;
		}

		public void start() {
			ySign = Math.signum(vy0);
			xSign = Math.signum(vx0);
			t = 0;
			start = System.currentTimeMillis();
			doStep();
		}

		private void doStep() {
			if (stop)
				return;
			float tsec = t / 1000f;
			vy = ay * tsec + vy0;
			vx = ax * tsec + vx0;
			if (Math.signum(vy) != ySign || Math.signum(vx) != xSign)
				return;
			float y = 0.5f * ay * tsec * tsec + vy0 * tsec + y0;
			float x = 0.5f * ax * tsec * tsec + vx0 * tsec + x0;
			mv.setdY((int) y);
			mv.setdX((int) x);
			mv.invalidate();
			t = System.currentTimeMillis() - start;
			if (stop)
				return;
			mv.post(new Runnable() {
				@Override
				public void run() {
					doStep();
				}
			});
		}
	}

}
